module sw.bugtracking.bugtrackingmodelist;
///Bugs:BugTrackingModeList.prefixを使ってない所がある。
version(BugTracking):
import nazo.http, nazo.date, std.string, std.conv, nazo.xhtml, std.uri, std.regexp, nazo.array;
import sw.mode, sw.contentmode, sw.cgiinfo, sw.siteconfig, sw.locale.locale, sw.unsupportedmode, sw.client, sw.bugtracking.bugtrackingdat, sw.bugtracking.bugtrackingdatlist, sw.modelist;
alias std.string.split split;

/**
 * Bugモードリスト
 */
class BugTrackingModeFactory:ModeFactory{
  mixin TModeFactory!();
  const static string prefix="bug";
  IMode getMode(string name){
    switch(name){
      case "new":
        return new NewMode(cfg);
      case "create":
        return new CreateMode(cfg);
      case "list":
      case "":
        return new ListMode(cfg);
      case "page":
        return new PageMode(cfg);
      case "change":
        return new ChangeMode(cfg);
      case "comment":
        return new CommentMode(cfg);
      case "commentdelete":
        return new CommentDeleteMode(cfg);
      default:
    }
    return new UnsupportedMode(cfg);
  }
}

class NewMode:XhtmlMode!(){
  this(SiteConfig* cfg){
    super(cfg);
  }
  override d_time lastModified(){
    return (new CgiInfo).datetime;
  }
  override int permission(){
    return Client.Authority.Guest | Client.Authority.Admin;
  }
  override string title(){return "新しいバグの登録";}
  override string contentBody(){
    string re="<p>\n<form action=\""~Http.relPath~"\" method=\"post\">\n<input type=\"hidden\" name=\"mode\" value=\"bug:create\"/>\n<label>バグの概略:<input type=\"text\" name=\"summary\"/></label><br/>\n<label>カテゴリー:<input type=\"text\" name=\"category\"/></label><br/>\n";
    if(Client.authority==Client.Authority.Admin){//初期ステータスを変更可能
      re~="<label>ステータス:<select name=\"status\">";
      foreach(Status.list sl;Status.initialList){
        re~="<option value=\""~Status.valueList[sl]~"\">"~Status.nameList[sl]~"</option>\n";
      }
      re~="</select></label>";
      re~="<label>サブステータス:<select name=\"substatus\">";
      foreach(SubStatus.list sl;SubStatus.initialList){
        re~="<option value=\""~SubStatus.valueList[sl]~"\">"~SubStatus.nameList[sl]~"</option>\n";
      }
      re~="</select></label>";
      re~="<br/>\n";
    }//違うなら初期ステータスはUnconfirmed固定
    return re~"<label>バグの詳細:<textarea name=\"details\"/></label>\n<input type=\"submit\"/>\n</form>\n</p>";
  }
}

class CreateMode:XhtmlMode!(ModeMethod.POST){
  this(SiteConfig* cfg){
    super(cfg);
  }
  override string title(){return "バグを登録しました";}
  override string contentBody(){
    BugDat dat=(new BugTrackingList).createBug();
    dat.summary=decodeComponent(Http.query["summary"].replace("+"," "));
    dat.category=decodeComponent(Http.query["category"].replace("+"," "));
    if(Client.authority==Client.Authority.Admin){
      dat.status=Status.getId(Http.query["status"]);
      dat.subStatus=SubStatus.getId(Http.query["substatus"]);
    }else{
      dat.status=Status.list.Unconfirmed;
      dat.subStatus=SubStatus.list.WaitConfirm;
    }
    dat.details=decodeComponent(Http.query["details"].replace("+"," "));
    dat.flash();
    return "<p><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":page&amp;bugnum="~std.string.toString(dat.bugnum)~"\">Bug "~std.string.toString(dat.bugnum)~"</a>を登録しました。</p>\n<ul><li><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":list\">一覧</a></li></ul>";
  }
}

class PageMode:XhtmlMode!(){
  BugDat dat;
  this(SiteConfig* cfg){
    super(cfg);
    dat=(new BugTrackingList).getBug(.toUint(Http.query["bugnum"]));
  }
  override d_time lastModified(){
//リストの更新と比較する必要あり。
//    return dat.lastModified;
    return 0;
  }
  override int permission(){
    return Client.Authority.Guest | Client.Authority.Admin;
  }
  override string title(){return "バグ No."~std.string.toString(dat.bugnum)~" "~dat.summary;}
  override string contentBody(){
    RegExp bugreg=new RegExp("bug\\s?(\\d+)","gi");
    RegExp cmtreg=new RegExp("&gt;&gt;(\\d+)","g");
    RegExp urlreg=new RegExp("(https?://[^\n^\r]+)","g");
    string msg(string str){//関数名が思い付かない。
      return bugreg.replace(cmtreg.replace(urlreg.replace(Escape.text(str).replace("\n","<br/>"),"<a href=\"$1\">$1</a>"),"<a href=\"#comment$1\">&gt;&gt;$1</a>"),"<a href=\""~Http.relPath~"?mode=bug:page&amp;bugnum=$1\">Bug $1</a>");
    }
    bool isAdmin=(Client.authority==Client.Authority.Admin);
    string re;
    if(isAdmin)re~="<form action=\""~Http.relPath~"\" method=\"post\">\n<input type=\"hidden\" name=\"mode\" value=\"bug:change\"/>\n<input type=\"hidden\" name=\"bugnum\" value=\""~std.string.toString(dat.bugnum)~"\"/>";
    re~="<dl>\n";
    re~="<dt>バグナンバー</dt><dd>"~std.string.toString(dat.bugnum)~"</dd>";
    re~="<dt>概要</dt><dd>"~(isAdmin?"<input name=\"summary\" value=\"":"")~Escape.arg(dat.summary)~(isAdmin?"\"/>":"")~"</dd>";
    re~="<dt>詳細</dt><dd>"~(isAdmin?"<textarea name=\"details\">"~Escape.text(dat.details)~"</textarea>":msg(dat.details))~"</dd>";
    re~="<dt>カテゴリー</dt><dd>"~(isAdmin?"<input name=\"category\" value=\"":"")~Escape.arg(dat.category)~(isAdmin?"\"/>":"")~"</dd>";
    re~="<dt>ステータス</dt><dd>";
    if(isAdmin){
      re~="<select name=\"status\">";
      foreach(uint sl,string val;Status.valueList){
        re~="<option value=\""~val~"\"";
        if(sl==dat.status)re~=" selected=\"selected\"";
        re~=">"~Status.nameList[cast(Status.list)sl]~"</option>\n";
      }
      re~="</select>";
    }else{
      re~=Status.nameList[dat.status];
    }
    re~="</dd>";
    re~="<dt>サブステータス</dt><dd>";
    if(isAdmin){
      re~="<select name=\"substatus\">";
      foreach(uint sl,string val;SubStatus.valueList){
        re~="<option value=\""~val~"\"";
        if(sl==dat.subStatus)re~=" selected=\"selected\"";
        re~=">"~SubStatus.nameList[cast(SubStatus.list)sl]~"</option>\n";
      }
      re~="</select>";
    }else{
      re~=SubStatus.nameList[dat.subStatus];
    }
    re~="</dd>";
    re~="</dl>";
    if(isAdmin)re~="<input type=\"submit\"/></form>";
    re~="<div>";
    foreach(uint i,Comment* comment;dat.comments){
      re~="<dl id=\"comment"~std.string.toString(i+1)~"\"><dt>"~std.string.toString(i+1)~" 投稿者:"~Escape.text(comment.name)~" 投稿日時:"~toISO8610String(comment.datetime)~" <form action=\""~Http.relPath~" \" method=\"post\" class=\"inlineMenu\"><input type=\"hidden\" name=\"mode\" value=\"bug:commentdelete\"/><input type=\"hidden\" name=\"bugnum\" value=\""~std.string.toString(dat.bugnum)~"\"/><input type=\"hidden\" name=\"commentnum\" value=\""~std.string.toString(i+1)~"\"/><input type=\"submit\" value=\"削除\"/></form></dt><dd>"~msg(comment.message)~"</dd></dl>";
    }
    re~="<form action=\""~Http.relPath~"\" method=\"post\">\n<input type=\"hidden\" name=\"mode\" value=\"bug:comment\"/>\n<input type=\"hidden\" name=\"bugnum\" value=\""~std.string.toString(dat.bugnum)~"\"/>\n";
    if(!isAdmin)re~="<label>投稿者<input name=\"name\" value=\"名無しさん\"/></label>\n";
    re~="<label>コメント<textarea name=\"comment\"></textarea></label>\n<input type=\"submit\"/>\n</form>";
    re~="</div>";
    return re;
  }
}

class ChangeMode:XhtmlMode!(ModeMethod.POST){
  this(SiteConfig* cfg){
    super(cfg);
  }
  override string title(){return "バグを変更しました";}
  override string contentBody(){
    if(Client.authority!=Client.Authority.Admin)throw new SecurityException();
    string bugnum=Http.query["bugnum"];
    BugDat dat=(new BugTrackingList).getBug(.toUint(bugnum));
    dat.summary=decodeComponent(Http.query["summary"].replace("+"," "));
    dat.category=decodeComponent(Http.query["category"].replace("+"," "));
    dat.status=Status.getId(Http.query["status"]);
    dat.subStatus=SubStatus.getId(Http.query["substatus"]);
    dat.details=decodeComponent(Http.query["details"].replace("+"," "));
    dat.flash();
    return "<p><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":page&amp;bugnum="~bugnum~"\">Bug "~bugnum~"</a>を変更しました。</p>\n<ul><li><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":list\">一覧</a></li></ul>";
  }
}

class ListMode:XhtmlMode!(){
  BugTrackingList btl;
  this(SiteConfig* cfg){
    super(cfg);
    btl=new BugTrackingList;
  }
  override d_time lastModified(){
    return btl.buglist.lastModified();
  }
  override int permission(){
    return Client.Authority.Guest | Client.Authority.Admin;
  }
  override string title(){return "バグ一覧";}
  override string contentBody(){
    string* t;
    t="category" in Http.query;
    string category=t?*t:"";
    t="status" in Http.query;
    string status=t?*t:"";
    t="substatus" in Http.query;
    string substatus=t?*t:"";
    string re="<p><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":new\">バグ登録</a></p>\n<table>\n";
    re~="<tr><th>番号</th><th>概要</th><th>カテゴリー</th><th>ステータス</th><th>サブステータス</th></tr>\n";
    foreach(BugDat bl;btl.getList){
      if((category!=""&&!category.split(",").opIn_r(bl.category))||(status!=""&&!status.split(",").opIn_r(Status.valueList[bl.status]))||(substatus!=""&&substatus.split(",").opIn_r(SubStatus.valueList[bl.subStatus])))continue;
      re~="<tr><td><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":page&amp;bugnum="~std.string.toString(bl.bugnum)~"\">"~std.string.toString(bl.bugnum)~"</a></td><td>"~Escape.text(bl.summary)~"</td><td>"~Escape.text(bl.category)~"</td><td>"~Status.nameList[bl.status]~"</td><td>"~SubStatus.nameList[bl.subStatus]~"</td></tr>\n";
    }
    re~="</table>";
    return re;
  }
}

class CommentMode:XhtmlMode!(ModeMethod.POST){
  this(SiteConfig* cfg){
    super(cfg);
  }
  override string title(){return "バグにコメントを追加しました";}
  override string contentBody(){
    string* t="bugnum" in Http.query;
    if(!t)throw new SecurityException;
    string bugnum=*t;
    BugDat dat=(new BugTrackingList).getBug(.toUint(bugnum));
    dat.comments~=new Comment;
    string name;
    if(Client.authority!=Client.Authority.Admin)name=decodeComponent(Http.query["name"].replace("+"," "));
    dat.comments[$-1].newComment(cfg,name,decodeComponent(Http.query["comment"].replace("+"," ")));
    dat.flash();
    return "<p><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":page&amp;bugnum="~bugnum~"\">Bug "~bugnum~"</a>にコメントを追加しました。</p>\n<ul><li><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":list\">一覧</a></li></ul>";
  }
}

class CommentDeleteMode:XhtmlMode!(ModeMethod.POST){
  this(SiteConfig* cfg){
    super(cfg);
  }
  override string title(){return "バグからコメントを削除しました";}
  override string contentBody(){
    if(Client.authority!=Client.Authority.Admin)throw new SecurityException();
    auto t="bugnum" in Http.query;
    auto t2="commentnum" in Http.query;
    if(!t || !t2)throw new SecurityException;
    string bugnum=*t;
    BugDat dat=(new BugTrackingList).getBug(.toUint(bugnum));
    string commentnum=*t2;
    Comment* delcomm=dat.comments[.toUint(commentnum)-1];
    delcomm.name="あぼーん";
    delcomm.message="あぼーん";
    dat.flash();
    return "<p><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":page&amp;bugnum="~bugnum~"\">Bug "~bugnum~"</a>からコメント >>"~commentnum~"を削除しました。</p>\n<ul><li><a href=\""~Http.relPath~"?mode="~BugTrackingModeFactory.prefix~":list\">一覧</a></li></ul>";
  }
}
